package com.zfei.gfb.device.web;

import com.zfei.gfb.api.UserService;
import com.zfei.gfb.bean.Const;
import com.zfei.gfb.bean.Result;
import com.zfei.gfb.bean.Status;
import com.zfei.gfb.bean.StatusException;
import com.zfei.gfb.core.Auth;
import com.zfei.gfb.core.Conf;
import com.zfei.gfb.core.LogLevel;
import com.zfei.gfb.core.LogResult;
import com.zfei.gfb.device.AppContext;
import com.zfei.gfb.model.GfbCompany;
import com.zfei.gfb.model.GfbGuest;
import com.zfei.gfb.model.GfbUser;
import com.zfei.gfb.model.GfbVillager;
import com.zfei.gfb.util.ByteArrays;
import com.zfei.gfb.util.Strings;
import com.zfei.gfb.util.WebLogBean;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.WebUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author fgh
 */
public abstract class WebAdvisor {

    private static final Logger logger = LoggerFactory.getLogger(WebAdvisor.class);


    protected Object around(ProceedingJoinPoint jp) throws Throwable {
        AppContext context = AppContext.getInstance();
        WebLogBean logBean = WebLogBean.getLogBean();
        try {
            before(jp, context, logBean);
            Object result = jp.proceed();
            after(jp, context, logBean, result);
            return Result.of(result);
        } catch (StatusException e) {
            logger.error(e.getMessage(), e);
            e.printStackTrace();
            logBean.setCode(e.getStatusCode());
            logBean.setError(e.getReasonPhrase());
            return new Result(e);
        } catch (DuplicateKeyException e) {
            e.printStackTrace();
            logger.error(e.getMessage(), e);
            logBean.setCode(Status.RESET_CONTENT.value());
            logBean.setError(e.getMessage());
            return new Result(Status.RESET_CONTENT.value(), Status.RESET_CONTENT.getReasonPhrase());
        } catch (Throwable e) {
            e.printStackTrace();
            logger.error(e.getMessage(), e);
            logBean.setCode(Status.UNKNOWN.value());
            logBean.setError(e.getMessage());
            return new Result(Status.UNKNOWN.value(), e.getMessage());
        }
    }

    protected void before(JoinPoint jp, AppContext context, WebLogBean logBean) {
        Class<?> clazz = jp.getTarget().getClass();
        Method method = ((MethodSignature) jp.getSignature()).getMethod();
        check(clazz, method, context, logBean);
    }

    protected void after(JoinPoint jp, AppContext context, WebLogBean logBean, Object result) {
        Method method = ((MethodSignature) jp.getSignature()).getMethod();
        LogResult logResult = method.getAnnotation(LogResult.class);
        if (logResult != null && logResult.value() >= Conf.getInteger("LogLevel", LogLevel.TRACE)) {
//            logBean.setResult(result);
        }
    }

    protected void check(Class<?> clazz, Method method, AppContext context, WebLogBean logBean) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        ContentCachingRequestWrapper requestWrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class);
        String body = Strings.deserialize(requestWrapper != null ? requestWrapper.getContentAsByteArray() : ByteArrays.EMPTY);
        if (StringUtils.isBlank(body)) {
            body = ByteArrays.EMPTY_BODY;
        }
        logBean.setPayload(body);

        Set<Integer> authConfig = new HashSet<>();
        Auth classAuth = clazz.getAnnotation(Auth.class);
        Auth methodAuth = method.getAnnotation(Auth.class);

        if (classAuth != null) {
            List<Integer> list = Arrays.stream(classAuth.type()).boxed().collect(Collectors.toList());
            authConfig.addAll(list);
        }
        if (methodAuth != null) {
            List<Integer> list = Arrays.stream(methodAuth.type()).boxed().collect(Collectors.toList());
            authConfig.addAll(list);
        }

        if (authConfig.isEmpty()) {
            return;
        }

        boolean access = false;
        for (int type : authConfig) {
            boolean b = checkAuthTyppe(type, request.getSession());
            if (b) {
                access = true;
                break;
            }
        }
        if (!access) {
            throw new StatusException(Status.UNAUTHORIZED);
        }
    }


    private boolean checkAuthTyppe(int type, HttpSession session) {

        if (type == Const.GUEST) {
            GfbGuest guest = (GfbGuest) session.getAttribute(AppContext.SESSION_H5_GUEST);
            if (guest != null) {
                AppContext.getInstance().setUid(guest.getId());
                AppContext.getInstance().setType(Const.GUEST);
                AppContext.getInstance().setIdentifierId(Const.GUEST + "_" + guest.getId());
                return true;
            }
        }

        if (type == Const.COMPANY) {
            GfbCompany company = (GfbCompany) session.getAttribute(AppContext.SESSION_H5_COMPANY);
            if (company != null) {
                AppContext.getInstance().setUid(company.getId());
                AppContext.getInstance().setType(Const.COMPANY);
                AppContext.getInstance().setIdentifierId(Const.COMPANY + "_" + company.getId());
                return true;
            }
        }

        if (type == Const.VILLAGER) {
            GfbVillager villager = (GfbVillager) session.getAttribute(AppContext.SESSION_H5_VILLAGER);
            if (villager != null) {
                AppContext.getInstance().setUid(villager.getId());
                AppContext.getInstance().setType(Const.VILLAGER);
                AppContext.getInstance().setIdentifierId(Const.VILLAGER + "_" + villager.getId());
                return true;
            }
        }

        if (type == Const.TOWN_MANAGER || type == Const.VILLAGE_MANAGER) {
            GfbUser user = (GfbUser) session.getAttribute(AppContext.SESSION_WEB_USER);
            if (user != null) {
                AppContext.getInstance().setUid(user.getId());
                if (type == Const.TOWN_MANAGER) {
                    AppContext.getInstance().setType(Const.TOWN_MANAGER);
                } else {
                    AppContext.getInstance().setType(Const.VILLAGE_MANAGER);
                }
                AppContext.getInstance().setIdentifierId(type + "_" + user.getId());
                return true;
            }
        }
        return false;
    }

    protected abstract UserService getUserService();
}
